package com.zxy.supplier_system.server.controller

import com.zxy.supplier_system.server.controller.GoodsController.GoodsDetail.SubGoodsResponse
import com.zxy.supplier_system.server.entity.Authority
import com.zxy.supplier_system.server.entity.Employee
import com.zxy.supplier_system.server.entity.Goods
import com.zxy.supplier_system.server.entity.SubGoods
import com.zxy.supplier_system.server.repository.GoodsSummaryWithMarketInStore
import com.zxy.supplier_system.server.repository.QueryGoodsUnitResponse
import com.zxy.supplier_system.server.service.GoodsService
import com.zxy.supplier_system.server.service.GoodsUnitService
import com.zxy.supplier_system.server.service.SubGoodsService
import com.zxy.supplier_system.server.utils.*
import org.apache.poi.ss.usermodel.WorkbookFactory
import org.springframework.beans.factory.annotation.Value
import org.springframework.core.io.InputStreamResource
import org.springframework.core.io.Resource
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.data.domain.Sort
import org.springframework.web.bind.annotation.*
import org.springframework.web.multipart.MultipartFile
import java.math.BigDecimal
import java.time.OffsetDateTime
import java.time.Period

@RestController
@RequestMapping("goods")
class GoodsController(
    private val goodsService: GoodsService,
    @Value("classpath:excel/templates/导入商品模板.xlsx")
    private val importGoodsTemplate: Resource,
    @Value("classpath:excel/templates/导入子商品模板.xlsx")
    private val importSubGoodsTemplate: Resource,
    private val goodsUnitService: GoodsUnitService,
    private val subGoodsService: SubGoodsService,
) {


    @GetMapping("units")
    fun getAllUnits(): List<String> {
        return goodsUnitService.getAllUnits()
    }


    data class AddGoodsRequest(
        val name: String,
        val price: BigDecimal,
        val amount: Int,
        val barcode: String,
        val categoryId: Int?,
        val brandId: Int?,
        val boxSpecification: String?,
        val units: List<EditGoodsUnitRequest>?,
        val costPrice: BigDecimal?,
        val registrationCertificateNumber: String? = null,
        val recordFilingNumber: String? = null,
        val expressionPeriod: Period? = null,
        val note: String? = null,
        val executiveStandard: String? = null,
        val subGoodsList: List<SubGoods> = emptyList(),
    ) {
        data class SubGoods(
            val id: Int?,
            val name: String,
            val number: String,
            val amount: Int,
        )
    }

    data class EditGoodsUnitRequest(
        val name: String,
        var id: Long?,
        val basicMultiple: Int = 0
    )

    @HasAuthority(Authority.货品_新增)
    @PostMapping
    fun addGoods(@RequestBody addGoodsRequest: AddGoodsRequest) {
        return this.goodsService.addGoods(addGoodsRequest)
    }

    data class QueryGoodsRequest(
        val name: String?,
        val barcode: String?,
        val categoryIds: List<Int> = emptyList(),
        val brandIds: List<Int> = emptyList(),
        val nameOrBarcode: String?,
        val factoryIds: List<Int> = emptyList(),
        val editMarketOrderId: Int?,
        val onlyNew: Boolean = false,
        val editMarketDeliveryOrderId: Int?,
        val editCheckInventoryRecordId: Int?,
        val excludeIds: List<Int> = emptyList(),
        val editMarketCheckInventoryRecordId: Int?,
        val editProcurementId: Int?,
        val editProcurementReceiptId: Int?,
        val disable:Boolean?,
    ) {
        val forEdit: Boolean
            get() = editMarketOrderId != null || editMarketDeliveryOrderId != null || editCheckInventoryRecordId != null || editMarketCheckInventoryRecordId != null || editProcurementId != null || editProcurementReceiptId != null
    }

    @HasAuthority(Authority.货品_查询)
    @GetMapping
    fun queryGoods(queryGoodsRequest: QueryGoodsRequest, pageable: Pageable): Page<QueryGoodsResponse> {
        return this.goodsService.queryGoods(queryGoodsRequest, pageable)
    }

    data class QueryGoodsResponse(
        val id: Int,
        val name: String,
        val barcode: String,
        val price: BigDecimal,
        val amount: Int,
        val createdDateTime: OffsetDateTime,
        val lastModifiedDateTime: OffsetDateTime?,
        val brand: IntIdAndName?,
        val category: IntIdAndName?,
        val factory: IntIdAndName?,
        val boxSpecification: String?,
        val costPrice: BigDecimal?,
        val units: List<GoodsUnitResponse>,
        val registrationCertificateNumber: String? = null,
        val recordFilingNumber: String? = null,
        val expressionPeriod: Period? = null,
        val executiveStandard: String? = null,
        val subGoodsAmount: Int = 0,
        val subGoodsList: List<SubGoodsResponse> = emptyList(),
        val compositeGoodsAmount: Int = 0,
        val disable: Boolean,
    ) {
        val unitNames: String
            get() = this.units.joinToString(", ") { it.name }
    }


    data class GoodsUnitResponse(
        val id: Long,
        val name: String,
        val basicMultiple: Int,
        val updatable: Boolean,
    )

    class ExportToExcelRequest(
        val fields: Array<GoodsExcelField> = arrayOf(
            GoodsExcelField.条码,
            GoodsExcelField.名称,
            GoodsExcelField.零售价,
            GoodsExcelField.单位,
            GoodsExcelField.库存,
            GoodsExcelField.品牌,
            GoodsExcelField.类别,
            GoodsExcelField.厂商,
            GoodsExcelField.箱规,
        )
    )

    @HasAuthority(Authority.货品_查询)
    @GetMapping("excel")
    fun exportGoodsToExcel(
        request: ExportToExcelRequest,
        queryGoodsRequest: QueryGoodsRequest,
        sort: Sort,
    ): Resource {
        return this.goodsService.exportGoodsToExcel(request.fields, queryGoodsRequest, sort)
    }


    @HasAuthority(Authority.货品_查询)
    @GetMapping(params = ["projection=basic"])
    fun queryGoodsSummaries(queryGoodsRequest: QueryGoodsRequest, pageable: Pageable): Page<GoodsBasic> {
        return this.goodsService.queryGoodsSummaries(queryGoodsRequest, pageable)
    }

    data class GoodsBasic(
        val id: Int,
        val name: String,
        val barcode: String,
        val price: BigDecimal,
        val amount: Int,
    )

    @HasAuthority(Authority.货品_修改)
    @PutMapping("{id}")
    fun updateGoods(@PathVariable id: Int, @RequestBody addGoodsRequest: AddGoodsRequest) {
        this.goodsService.updateGoods(id, addGoodsRequest)
    }

    data class BatchUpdateGoodsRequest(
        val goodsIds:List<Int>,
        val categoryId: Int?,
        val brandId: Int?,
        val boxSpecification: String?,
        val expressionPeriod: Period?,
        val amount: Int?,
        val costPrice: BigDecimal?,
        val price: BigDecimal?,
    )

    @PutMapping
    fun batchUpdateGoods(@RequestBody request: BatchUpdateGoodsRequest){
        this.goodsService.batchUpdateGoods(request)
    }

    @HasAuthority(Authority.货品_删除)
    @DeleteMapping("{id}")
    fun deleteGoods(@PathVariable id: Int) {
        this.goodsService.deleteGoods(id)
    }


    @HasAnyAuthority(
        Authority.卖场_订单_下单,
        Authority.卖场_订单_转化为送货单,
        Authority.卖场_送货单_修改,
        Authority.卖场_送货单_验收,
        Authority.卖场_送货单_审核,
        Authority.采购_下单,
        Authority.采购_入库_入库,
    )
    @GetMapping(params = ["projection=summary"])
    fun getAllGoodsSummaries(queryGoodsRequest: QueryGoodsRequest): List<GoodsSummary> {
        return this.goodsService.getAllGoodsSummaries(queryGoodsRequest)
    }

    @GetMapping(params = ["projection=summary","page"])
    fun queryGoodsSummariesByPage(queryGoodsRequest: QueryGoodsRequest,pageable: Pageable): Page<GoodsSummary>{
        return this.goodsService.queryGoodsSummariesByPage(queryGoodsRequest,pageable)
    }


    @HasAnyAuthority(
        Authority.卖场_订单_下单,
        Authority.卖场_订单_转化为送货单,
        Authority.卖场_送货单_修改,
        Authority.卖场_送货单_验收,
        Authority.卖场_送货单_审核,
    )
    @GetMapping(params = ["marketId"])
    fun getAllGoodsSummaryWithMarketInStore(marketId: Int): List<GoodsSummaryWithMarketInStore> {
        return this.goodsService.getAllGoodsSummaryWithMarketInStore(marketId)
    }

    @HasAnyAuthority(
        Authority.卖场_退货单_下单,
        Authority.卖场_退货单_修改,
    )
    @GetMapping(params = ["marketId", "onlyInMarket=true"])
    fun getAllGoodsSummaryWithMarketInStoreInMarket(marketId: Int): List<GoodsSummaryWithMarketInStore> {
        return this.goodsService.getAllGoodsSummaryWithMarketInStoreInMarket(marketId)
    }

    data class GoodsSummary(
        val id: Int,
        val name: String,
        val barcode: String,
        val units: List<QueryGoodsUnitResponse>,
        val subGoodsList: List<SubGoodsResponse>,
    )

    @GetMapping("barcodes/{barcode}/exists")
    fun exists(@PathVariable barcode: String): Boolean {
        return this.goodsService.exists(barcode)
    }

    @HasAuthority(Authority.货品_导入)
    @GetMapping("excel/import/template")
    fun downloadImportTemplate(): InputStreamResource {
        return InputStreamResource(importGoodsTemplate.inputStream)
    }

    @HasAuthority(Authority.货品_导入)
    @PostMapping("excel/import")
    fun importFromExcel(@RequestParam file: MultipartFile): Int {
        val workbook = WorkbookFactory.create(file.inputStream)
        val sheet = workbook.getSheetAt(0)

        val goodsItems = (1..sheet.lastRowNum).map(sheet::getRow).mapNotNull { row ->
            fun getCellValue(cellIndex: Int): String? {
                return row.getCell(cellIndex)?.displayCellString?.trim()
            }

            var cellIndex = 0
            val name = getCellValue(cellIndex++) ?: return@mapNotNull null
            val barcode = getCellValue(cellIndex++) ?: return@mapNotNull null
            val price = getCellValue(cellIndex++)?.toDoubleOrNull() ?: return@mapNotNull null
            val category = getCellValue(cellIndex++)
            val brand = getCellValue(cellIndex++)
            val amount = getCellValue(cellIndex++)?.toDoubleOrNull()?.toInt() ?: 0
            val specificationBox = getCellValue(cellIndex++)
            val unit = getCellValue(cellIndex++)
            val registrationCertificateNumber = getCellValue(cellIndex++)
            val recordFilingNumber = getCellValue(cellIndex++)
            val expressionPeriod = getCellValue(cellIndex)
            CreateGoodsItem(
                name,
                barcode,
                category,
                brand,
                price,
                amount,
                specificationBox,
                unit,
                registrationCertificateNumber,
                recordFilingNumber,
                expressionPeriod?.let {
                    periodFromSpokenLanguage(it)
                }
            )
        }

        return this.goodsService.importFromExcelItems(goodsItems)
    }

    data class CreateGoodsItem(
        val name: String,
        val barcode: String,
        val category: String?,
        val brand: String?,
        val price: Double,
        val amount: Int? = 0,
        val boxSpecification: String?,
        val unit: String?,
        val registrationCertificateNumber: String?,
        val recordFilingNumber: String?,
        val expressionPeriod: Period?,
    )

    @HasAuthority(Authority.货品_导入)
    @GetMapping("sub/excel/import/template")
    fun downloadSubGoodsImportTemplate(): InputStreamResource {
        return InputStreamResource(importSubGoodsTemplate.inputStream)
    }

    data class AddSubGoodsItem(
        val number: String,
        val name: String,
        val barcode: String,
        val amount: Int = 0,
    )

    @HasAuthority(Authority.货品_导入)
    @PostMapping("sub/excel/import")
    fun importSubGoodsFromExcel(@RequestParam file: MultipartFile): Int {
        val workbook = WorkbookFactory.create(file.inputStream)
        val sheet = workbook.getSheetAt(0)

        val subGoodsItems = (1..sheet.lastRowNum).map(sheet::getRow).mapNotNull { row ->
            fun getCellValue(cellIndex: Int): String? = row.getCell(cellIndex)?.displayCellString?.trim()
            var cellIndex = 0
            val number = getCellValue(cellIndex++) ?: return@mapNotNull null
            val name = getCellValue(cellIndex++) ?: return@mapNotNull null
            val barcode = getCellValue(cellIndex++) ?: return@mapNotNull null
            val amount = getCellValue(cellIndex)?.toDoubleOrNull()?.toInt() ?: 0
            AddSubGoodsItem(
                number,
                name,
                barcode,
                amount,
            )
        }

        return this.subGoodsService.importSubGoodsFromExcelItems(subGoodsItems)
    }

    @GetMapping("{id}")
    fun getGoodsDetail(@PathVariable id: Int): GoodsDetail {
        return this.goodsService.getGoodsDetail(id)
    }

    @GetMapping("barcode/{barcode}")
    fun getGoodsDetail(@PathVariable barcode: String): GoodsDetail {
        return this.goodsService.getGoodsDetail(barcode)
    }

    data class GoodsDetail(
        val id: Int,
        val name: String,
        val barcode: String,
        val price: BigDecimal,
        val costPrice:BigDecimal?,
        val amount: Int,
        val createdDateTime: OffsetDateTime,
        val brand: IntIdAndName?,
        val category: IntIdAndName?,
        val factory: IntIdAndName?,
        val boxSpecification: String?,
        val lastCheckInventoryRecordItem: LastCheckInventoryRecordItem?,
        val units: List<GoodsUnitResponse>,
        val recordFilingNumber: String?,
        val registrationCertificateNumber: String?,
        val expressionPeriod: Period?,
        val executiveStandard: String?,
        val subGoodsList: List<SubGoodsResponse> = emptyList(),
        val note: String? = null,
    ) {

        companion object{
            fun fromGoods(goods: Goods,employee: Employee): GoodsDetail{
                return GoodsDetail(
                    goods.id,
                    goods.name,
                    goods.barcode,
                    goods.price,
                    if (employee.authorities.contains(Authority.货品_进价_查看)) {
                        goods.costPrice
                    }else null,
                    goods.amount,
                    goods.createdDateTime,
                    goods.brand?.toIntIdAndName(),
                    goods.category?.toIntIdAndName(),
                    goods.factory?.toIntIdAndName(),
                    goods.boxSpecification,
                    if (employee.authorities.contains(Authority.卖场_盘点_查询))
                        goods.checkInventoryRecordItems.lastOrNull()?.let {
                            LastCheckInventoryRecordItem(
                                it.record.id,
                                it.amount,
                                it.record.createdDateTime
                            )
                        } else null,
                    goods.units.map {
                        GoodsUnitResponse(it.id!!, it.name, it.basicMultiple, it.updatable)
                    },
                    goods.recordFilingNumber,
                    goods.registrationCertificateNumber,
                    goods.expressionPeriod,
                    goods.executiveStandard,
                    goods.subGoodsList.map {
                        SubGoodsResponse(it.id!!, it.name, it.number, it.amount)
                    },
                    goods.note,
                )
            }
        }
        data class SubGoodsResponse(
            val id: Int,
            val name: String,
            val number: String,
            val amount: Int,
        ){
            companion object{
                fun fromSubGoods(subGoods: SubGoods): SubGoodsResponse{
                    return SubGoodsResponse(
                        subGoods.id!!,
                        subGoods.name,
                        subGoods.number,
                        subGoods.amount,
                    )
                }
            }
        }

        data class LastCheckInventoryRecordItem(
            val checkInventoryRecordId: Int,
            val amount: Int,
            val createdDateTime: OffsetDateTime,
        )
    }

    @PatchMapping("{id}/disable")
    fun disableGoods(@PathVariable id:Int){
        this.goodsService.disableGoods(id)
    }

    @PatchMapping("{id}/enable")
    fun enableGoods(@PathVariable id:Int){
        this.goodsService.enableGoods(id)
    }

    @PatchMapping("batch/enable")
    fun batchEnableGoods(@RequestParam goodsIds: List<Int>){
        this.goodsService.batchEnableGoods(goodsIds)
    }
    @PatchMapping("batch/disable")
    fun batchDisableGoods(@RequestParam goodsIds: List<Int>){
        this.goodsService.batchDisableGoods(goodsIds)
    }

}
